#include "Excel.h"
#include <zip.h>
#include <iostream>

namespace Excel
{

#define LogError(Value) std::cerr << "[Excel.cpp:" << __LINE__ << "]" << Value << "\n"

Excel::Excel()
{

}

Excel::~Excel()
{

}

bool Excel::read(const std::string &excelFile)
{
    //1.
    int err(0);
    auto zip = zip_open(excelFile.c_str(), 0, &err);
    if (!zip)
    {
        LogError("加载Excel文件失败:" + excelFile + ",错误码:" + std::to_string(err));
        return false;
    }

    //2.
    const zip_int64_t num = zip_get_num_entries(zip, 0);
    if (num < 0)
    {
        LogError("读取文件数量有误");
        zip_close(zip);
        return false;
    }

    //3.
    initSharedStr(zip);

    //4.
    vecSheet_.clear();
    for (zip_int64_t i = 0; i != num; ++i)
    {
        std::string name(zip_get_name(zip, i, 0));
        if (name.size() > 14 && name.find("xl/worksheets/") == 0 && name.rfind(".xml") + 4 == name.size())
        {
            name = name.substr(14, name.size() - 18);
            addSheet(zip, static_cast<uint32_t>(i), name);
        }
    }

    //5.
    zip_close(zip);
    vecSharedStr_.clear();
    return true;
}

const vecSheet_t &Excel::getAllSheet() const
{
    return vecSheet_;
}

void Excel::initSharedStr(void *zip)
{
    //1.
    vecSharedStr_.clear();

    //2.
    zip_stat_t st;
    if(zip_stat(static_cast<zip_t *>(zip), "xl/sharedStrings.xml", 0, &st))
    {
        return;
    }

    //3.
    auto fr = zip_fopen(static_cast<zip_t *>(zip), "xl/sharedStrings.xml", 0);
    if (!fr)
    {
        return;
    }

    //4.
    std::string s;
    s.resize(st.size);
    if (zip_fread(fr, (char *)s.data(), st.size) != static_cast<zip_int64_t>(st.size))
    {
        LogError("读取共享字符串出错");
        zip_fclose(fr);
        return;
    }
    else
    {
        zip_fclose(fr);
    }

    //5.
    std::size_t i, j;
    i = s.find("<t>");
    while (i != std::string::npos)
    {
        //5.1
        i += 3;
        j = s.find("</t>", i);
        if (j == std::string::npos)
        {
            LogError("共享字符串格式有误");
            break;
        }

        //5.2
        vecSharedStr_.push_back(s.substr(i, j - i));

        //5.3
        i = s.find("<t>", j + 4);
    }
}

void Excel::addSheet(void *zip, uint32_t index, const std::string &name)
{
    //1.
    zip_stat_t st;
    if (zip_stat_index(static_cast<zip_t*>(zip), index, 0, &st))
    {
        LogError("获取文件长度失败");
        return;
    }

    //2.
    auto fr = zip_fopen_index(static_cast<zip_t*>(zip), index, 0);
    if (!fr)
    {
        LogError("打开文件失败");
        return;
    }

    //3.
    std::string s;
    s.resize(st.size);
    if (zip_fread(fr, (char *)s.data(), st.size) != static_cast<zip_int64_t>(st.size))
    {
        LogError("读取数据出错");
        zip_fclose(fr);
        return;
    }
    else
    {
        zip_fclose(fr);
    }

    //4.
    auto sheet = std::make_shared<Sheet_t>(name);
    vecSheet_.push_back(sheet);
    parseSheet(sheet, s);
}

void Excel::parseSheet(std::shared_ptr<Sheet_t> &sheet, const std::string &text) const
{
    std::string_view sw(text), key, value, stype;
    std::size_t i, j;
    i = sw.find("<c r=");
    while (i != std::string::npos)
    {
        //1.
        i += 6;
        j  = sw.find('"', i);
        if (j == std::string::npos)
        {
            LogError("数据有误");
            return;
        }
        key = sw.substr(i, j - i);

        //2.
        ++j;
        i   = sw.find('>', j);
        if (i == std::string::npos)
        {
            LogError("数据有误");
            return;
        }
        stype = sw.substr(j, i - j);

        //3.
        ++i;
        j = sw.find("</", i);
        if (j == std::string::npos)
        {
            LogError("数据有误");
            return;
        }
        value = sw.substr(i, j - i);
        j += 2;

        //4.
        if (text[j] == 'v')
        {
            i = value.rfind("<v>");
        }
        else if(text[j] == 't')
        {
            i = value.rfind("<t>");
        }
        else
        {
            LogError("需要调试添加其它关键字");
            return;
        }

        //5.
        if (i == std::string::npos)
        {
            LogError("数据有误");
            return;
        }
        value = value.substr(i + 3);

        //6.
        std::string sKey(key);
        auto res  = paraseIndex(sKey);
        auto cell = std::make_shared<Cell_t>(res.first, res.second, sKey, "");
        if (stype.find("t=\"s\"") != std::string::npos)
        {
            cell->value = vecSharedStr_[std::stoi(std::string(value))];//注意,这里不检查索引的正确性
        }
        else
        {
            cell->value = value;
        }
        sheet->mapKey[cell->key] = cell;
        sheet->mapValue[cell->row][cell->col] = cell;

        //7.
        i = sw.find("<c r=", j + 1);
    }
}

std::pair<uint32_t, uint32_t> Excel::paraseIndex(const std::string &s) const
{
    //1.
    std::pair<uint32_t, uint32_t> ret{0,0};
    std::size_t n = s.size();
    if (n < 2)
    {
        return ret;
    }

    //2.
    while (n != 1)
    {
        --n;
        if (s[n] < 'A')
        {
            continue;
        }

        ++n;
        break;
    }
    
    //3.
    ret.first = std::stoi(s.substr(n));
    uint32_t mul = 1;
    while (n)
    {
        --n;
        ret.second += (s[n] - 'A' + 1) * mul;
        mul *= 26;
    }
    
    return ret;
}

}